package org.fpdev.apps.rtemaster.shp;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.fpdev.core.basenet.LinkTypes;
import org.fpdev.apps.rtemaster.shp.ShpImporter.FacilityTypeRule;
import org.opengis.feature.simple.SimpleFeature;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class ShpImportProfile implements Comparable {

  private String name_;
  private List<ShpImporter.FacilityTypeRule> facTypeRules_;

  // dictionary for mapping generalized attribute role to actual attribute name in table
  private Map<AttributeRole, String> attrs_;

  /** node topology fields **/

  private boolean autoAssignNodeIDs_; // whether or not node IDs are assigned automatically upon import

  // if autoAssignNodeIDs_ = false, these fields are used:
  private String fNodeIDAttrName_;
  private String tNodeIDAttrName_;

  // if autoAssignNodeIDs_ = true, these fields are used:
  private int firstNodeID_;
  private double nodeTolerance_;

  /** link topology fields **/

  private boolean autoAssignLinkIDs_;
  private String linkIDAttrName_;

  // used if autoAssignLinkIDs_ = false
  private int firstLinkID_;


  /** enumeration for attribute role types **/
  public enum AttributeRole {

    FNODE_ID("fNodeID"), TNODE_ID("tNodeID"), LINK_ID("linkID"), LINK_NAME("linkName"), FADDRL("fAddrL"), FADDRR("fAddrR"), TADDRL("tAddrL"), TADDRR("tAddrR"), ZIPL("zipL"), ZIPR("zipR");
    private final String forXml;

    // name as to be rendered in xml file
    AttributeRole(String forXml) {
      this.forXml = forXml;
    }
  } // end of AttributeRole enum definition

  public static AttributeRole getAttributeRoleFromString(String str) {
    if (str.equals("fNodeID")) return AttributeRole.FNODE_ID;
    if (str.equals("tNodeID")) return AttributeRole.TNODE_ID;
    if (str.equals("linkID")) return AttributeRole.LINK_ID;
    if (str.equals("linkName")) return AttributeRole.LINK_NAME;
    if (str.equals("fAddrL")) return AttributeRole.FADDRL;
    if (str.equals("fAddrR")) return AttributeRole.FADDRR;
    if (str.equals("tAddrL")) return AttributeRole.TADDRL;
    if (str.equals("tAddrR")) return AttributeRole.TADDRR;
    if (str.equals("zipL")) return AttributeRole.ZIPL;
    if (str.equals("zipR")) return AttributeRole.ZIPR;
    return null;
  }

  public ShpImportProfile() {
    super();
    facTypeRules_ = new LinkedList<ShpImporter.FacilityTypeRule>();
    attrs_ = new HashMap<AttributeRole, String>();
  }

  public ShpImportProfile(Node profileNode) {
    super();
    if (!profileNode.getNodeName().equals("profile")) {
      return;
    }
    // TODO: throw exception
    name_ = profileNode.getAttributes().getNamedItem("name").getNodeValue();
    //System.out.println("reading profile "+name_);
    facTypeRules_ = new LinkedList<ShpImporter.FacilityTypeRule>();
    attrs_ = new HashMap<AttributeRole, String>();
    NodeList nodes = profileNode.getChildNodes();
    for (int i = 0; i < nodes.getLength(); i++) {
      Node node = nodes.item(i);
      String nodeName = node.getNodeName();
      if (nodeName.equals("topology")) {
        NodeList topNodes = node.getChildNodes();
        for (int it = 0; it < topNodes.getLength(); it++) {
          Node topNode = topNodes.item(it);
          NamedNodeMap attrs = topNode.getAttributes();
          if (topNode.getNodeName().equals("nodes_autoassign")) {
            autoAssignNodeIDs_ = true;
            firstNodeID_ = new Integer(attrs.getNamedItem("first").getNodeValue());
            nodeTolerance_ = new Double(attrs.getNamedItem("tol").getNodeValue());
          }
          if (topNode.getNodeName().equals("nodes_readfromattr")) {
            autoAssignNodeIDs_ = false;
            fNodeIDAttrName_ = attrs.getNamedItem("fattr").getNodeValue();
            tNodeIDAttrName_ = attrs.getNamedItem("tattr").getNodeValue();
          }
          if (topNode.getNodeName().equals("links_autoassign")) {
            autoAssignLinkIDs_ = true;
            firstLinkID_ = new Integer(attrs.getNamedItem("first").getNodeValue());
          }
          if (topNode.getNodeName().equals("links_readfromattr")) {
            autoAssignLinkIDs_ = false;
            linkIDAttrName_ = attrs.getNamedItem("attr").getNodeValue();
          }
        }
      }
      if (nodeName.equals("attrmapping")) {
        NodeList attrMapNodes = node.getChildNodes();
        for (int ia = 0; ia < attrMapNodes.getLength(); ia++) {
          Node attrMapNode = attrMapNodes.item(ia);
          if (attrMapNode.getNodeName().equals("attr")) {
            NamedNodeMap attrs = attrMapNode.getAttributes();
            String role = attrs.getNamedItem("role").getNodeValue();
            String name = attrs.getNamedItem("name").getNodeValue();
            this.setAttributeMapping(getAttributeRoleFromString(role), name);
          }
        }
      }
      if (nodeName.equals("factyperules")) {
        NodeList ruleNodes = node.getChildNodes();
        for (int ir = 0; ir < ruleNodes.getLength(); ir++) {
          Node ruleNode = ruleNodes.item(ir);
          if (ruleNode.getNodeName().equals("rule")) {
            NamedNodeMap attrs = ruleNode.getAttributes();
            this.createFacTypeRule(attrs.getNamedItem("attrname").getNodeValue(), attrs.getNamedItem("attrval").getNodeValue(), new Integer(attrs.getNamedItem("factype").getNodeValue()).intValue());
          }
        }
      }
    }
  }

  public boolean getAutoAssignNodeIDs() {
    return autoAssignNodeIDs_;
  }

  public String getFNodeIDAttrName() {
    return fNodeIDAttrName_;
  }

  public String getTNodeIDAttrName() {
    return tNodeIDAttrName_;
  }

  public int getFirstNodeID() {
    return firstNodeID_;
  }

  public double getNodeTolerance() {
    return nodeTolerance_;
  }

  public boolean getAutoAssignLinkIDs() {
    return autoAssignLinkIDs_;
  }

  public String getLinkIDAttrName() {
    return linkIDAttrName_;
  }

  public int getFirstLinkID() {
    return firstLinkID_;
  }

  public Collection<ShpImporter.FacilityTypeRule> getFacTypeRules() {
    return facTypeRules_;
  }

  public Set<Entry<AttributeRole, String>> getAttributeRoleMappings() {
    return attrs_.entrySet();
  }

  public String getAttributeNameForRole(AttributeRole role) {
    return attrs_.get(role);
  }

  public int resolveFacilityType(SimpleFeature feature) {
    for(FacilityTypeRule rule : getFacTypeRules()) {
      if(feature.getAttribute(rule.getAttrName()).toString().equals(rule.getAttrValue()))
        return rule.getFacilityType();
    }
    return LinkTypes.LOCAL_TWOWAY;
  }

  public void setName(String name) {
    name_ = name;
  }

  public String getName() {
    return name_;
  }

  public void assignNodeIDsFromField(String fieldNameF, String fieldNameT) {
    autoAssignNodeIDs_ = false;
    fNodeIDAttrName_ = fieldNameF;
    tNodeIDAttrName_ = fieldNameT;
  }

  public void autoAssignNodeIDs(int first, double tol) {
    autoAssignNodeIDs_ = true;
    firstNodeID_ = first;
    nodeTolerance_ = tol;
  }

  public void assignLinkIDsFromField(String fieldName) {
    autoAssignLinkIDs_ = false;
    linkIDAttrName_ = fieldName;
  }

  public void autoAssignLinkIDs(int first) {
    autoAssignLinkIDs_ = true;
    firstLinkID_ = first;
  }

  public void createFacTypeRule(String attrName, String attrVal, int facType) {
    facTypeRules_.add(new ShpImporter.FacilityTypeRule(attrName, attrVal, facType));
  }

  public void setAttributeMapping(AttributeRole role, String attrName) {
    attrs_.put(role, attrName);
  }

  public String getXml() {
    String xml = "";
    xml += "<profile name=\"" + name_ + "\">\n";
    xml += "  <topology>\n";
    if (autoAssignNodeIDs_) {
      xml += "    <nodes_autoassign first=\"" + firstNodeID_ + "\" tol=\"" + nodeTolerance_ + "\" />\n";
    } else {
      xml += "    <nodes_readfromattr fattr=\"" + fNodeIDAttrName_ + "\" tattr=\"" + tNodeIDAttrName_ + "\" />\n";
    }
    if (autoAssignLinkIDs_) {
      xml += "    <links_autoassign first=\"" + firstLinkID_ + "\" />\n";
    } else {
      xml += "    <links_readfromattr attr=\"" + linkIDAttrName_ + "\" />\n";
    }
    xml += "  </topology>\n";
    xml += "  <attrmapping>\n";
    for (Iterator<Map.Entry<AttributeRole, String>> it = attrs_.entrySet().iterator(); it.hasNext();) {
      Map.Entry<AttributeRole, String> entry = it.next();
      xml += "    <attr role=\"" + entry.getKey().forXml + "\" name=\"" + entry.getValue() + "\" />\n";
    }
    xml += "  </attrmapping>\n";
    xml += "  <factyperules>\n";
    for (Iterator<ShpImporter.FacilityTypeRule> it = facTypeRules_.iterator(); it.hasNext();) {
      ShpImporter.FacilityTypeRule rule = it.next();
      xml += "    <rule attrname=\"" + rule.getAttrName() + "\" attrval=\"" + rule.getAttrValue() + "\" factype=\"" + rule.getFacilityType() + "\" />\n";
    }
    xml += "  </factyperules>\n";
    xml += "</profile>\n";
    return xml;
  }

  public int compareTo(Object o) {
    return name_.compareTo(((ShpImportProfile) o).getName());
  }
}
